/**
 *  29/lug/07 - 17:36:04
 *  Create by hachreak
 *  Under GPL V2 license
 */

package it.secondlifelab.p2pSL.jserver;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.Comparator;
import java.util.Timer;
import java.util.TimerTask;

import com.Ostermiller.util.MD5;
import com.prolixtech.jaminid.ContentOracle;
import com.prolixtech.jaminid.Request;
import com.prolixtech.jaminid.Response;

/**
 * @author hachreak
 * 
 * p2p server over html
 */
public class Server extends ContentOracle {

	private Debug debug;

	private P2PSLconfig config;

	private DirectoryLoader dirloader;

	private P2PSLphpInterface p2pInt;

	private Timer timer;

	public Server(Debug debug2, P2PSLconfig config2) throws IOException {
		this.debug = debug2;
		this.config = config2;
		dirloader = new DirectoryLoader();
		p2pInt = new P2PSLphpInterface(debug, config);
		
		 timer = new Timer();
//		 aspetta 5 secondi prima dell'esecuzione,poi
//		 viene eseguita ogni 10 secondi
		timer.schedule( new TimerTask(){

			@Override
			public void run() {
				String md5 = config.getMD5();
				if(!md5.equals(""))
					new P2PSLphpInterface(debug, config).setIp(md5);
			}
			
		}, 50000, 2700000 ); 
	}

	/**
	 * Aggiorna la lista in caso di cambiamenti
	 * 
	 * @throws IOException
	 */
	public void updateSharedFiles() throws IOException {
		dirloader = new DirectoryLoader();
		// System.out.println("update");
		debug.print("[ OK ] Directory shared updated");
	}

	public String demultiplex(Request connRequest, Response connResponse) {
		String uri = connRequest.getURI();
		
		if (uri.equals("/list")) {
			return sendList();
		} else if (uri.equals("/file") || uri.equals("/file.php")) {
			sendFile(connRequest, connResponse);
		} else if (uri.equals("/link")) {
			try {
				return sendLink(connRequest, connResponse);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} else if (uri.startsWith("/md5")) {
			try {
				return sendMd5(connRequest, connResponse);
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
		} else if (uri.equals("/links")) {
			return sendLinks(connRequest, connResponse);
		} else if (uri.startsWith("/file/")) {
			sendFile(connRequest.unEscape(uri.replaceFirst("/file/", "")),
					connResponse);
		} else if (uri.equals("/download")) {
			try {
				return download(connRequest, connResponse);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} else if (uri.equals("/ping")) {
			return "pong";
		} else if (uri.equals("/getbymd5.php")) {
			File file = dirloader.getFile(connRequest.getParam("md5"));
			sendFile(file.getPath(),
					connResponse);
		} else if (uri.startsWith("/getbymd5/")) {
			File file = dirloader.getFile(uri.substring("/getbymd5/".length()));
			sendFile(file.getPath(),
					connResponse);
		} else if (uri.startsWith("/search")) {
			return this.searchSimple(connRequest);
		}
		// System.out.println();
		return "";// connRequest.getParam("prova");
	}

	protected String sendMd5(Request connRequest, Response connResponse)
			throws IOException {
		String file = connRequest.getParam("file");
		// Create link for download file
		// If is a file shared
		File f = new File(file);
		if (dirloader.containFile(f)) {
			debug.print("[ OK ] Request link with md5: " + file);
			return "http://" + p2pInt.getMyIp() + ":" + config.getPort()
					+ "/getbymd5/" + MD5.getHashString(f) + "\n";
		} else {
			debug.print("[ ERROR ] Request link: " + file);
		}

		return "";
	}

	protected String download(Request connRequest, Response connResponse)
			throws IOException {
		//String md5 = connRequest.getParam("md5");
		String server = connRequest.getParam("server");
		// Integer port = new Integer(connRequest.getParam("port"));
		// nome del file da salvare
		String file2save = connRequest.getParam("file2save");
		String file      = connRequest.getParam("file");
		String pax = connRequest.getParam("password_private");
		// String md5 = connRequest.getParam("md5");

		if (pax.equals(config.getPrivateMD5Password())) {

			URL download = new URL(server + "/file/" + file);

			DataInputStream is = new DataInputStream(download.openStream());

			DataOutputStream os = new DataOutputStream(new FileOutputStream(
					new File(file2save)));

			byte by;
			try {
				while ((by = is.readByte()) != -1) {
					os.writeByte(by);
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				debug.print("[ ERROR ] Download file: " + file);

				e.printStackTrace();
			}

			debug.print("[ OK ] Download file: " + file);

			return "ok";
		} else {
			debug.print("[ ERROR ] Can't download: " + file);

			return "[ ERROR ] can't download";
		}
	}

	protected String sendLinks(Request req, Response connResponse) {
		debug.print("Send list of links");

		String ret = "";

		try {
			String ip = p2pInt.getMyIp();

			ret = Utility.listFile2links(dirloader.getFiles(), "http://" + ip
					+ ":" + config.getPort());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			debug.print("[ ERROR ] Can't load list of files shared");
			e.printStackTrace();
		}

		return ret;
	}

	protected void sendFile(String file, Response connResponse) {
		File f = null;
		try {
			f = new File(file);

			debug.print("Request file: " + f);

			// If is a file shared
			if (dirloader.containFile(f)) {
				connResponse.sendFile(f);
			}

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * Send list of file shared
	 * 
	 * @param os
	 *            output stream
	 * @return
	 * @throws IOException
	 */
	protected String sendList() {
		debug.print("Send list");

		String ret = "";

		try {
			ret = Utility.listFile2string(dirloader.getFiles());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			debug.print("[ ERROR ] Can't load list of files shared");
			e.printStackTrace();
		}

		return ret;
	}

	/**
	 * Send file shared
	 * 
	 * @param os
	 *            output stream
	 * @throws IOException
	 */
	protected void sendFile(Request req, Response connResponse) {
		String file = req.getParam("file");
		String md5 = req.getParam("md5");
		//System.out.println(file);
		File f = null;
		try {
			if (!md5.equals("")) {
				f = dirloader.getFile(md5);
			} else if(!file.equals("")){
				f = new File(file);
			}else{
				debug.print("[ ERROR ] Request null file");
				return;
			}
			// If is a file shared
			if (dirloader.containFile(f)) {
				debug.print("Request file: " + f);
				connResponse.sendFile(f);
				debug.print("[ OK ] File sended: " + f);
			}else{
				debug.print("[ ERROR ] File not shared: "+f.getPath());
			}

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			debug.print("[ ERROR ] Can't send file: "+f.getPath());
		}
	}

	/**
	 * Send link to a file
	 * 
	 * @param os
	 *            output stream
	 * @param line
	 *            name of file
	 * @throws IOException
	 */
	protected String sendLink(Request req, Response connResponse)
			throws IOException {
		String file = req.getParam("file");
		// Create link for download file
		// If is a file shared
		if (dirloader.containFile(new File(file))) {
			debug.print("[ OK ] Request link: " + file);
			return "http://" + p2pInt.getMyIp() + ":" + config.getPort()
					+ "/file/" + file + "\n";
		} else {
			debug.print("[ ERROR ] Request link: " + file);
		}

		return "";
	}

	/**
	 * Send list of file shared by search
	 * 
	 * @param os
	 *            output stream
	 * @return
	 * @throws IOException
	 */
	protected String searchSimple(Request req) {
		String pattern = req.getParam("pattern");
		
		debug.print("Search (simple) "+pattern);

		String ret = "";

		try {
			ret = Utility.listFile2string(dirloader.search(pattern, new Comparator<String>(){

				public int compare(String file, String pattern) {
					if(file.contains(pattern))
						return 0;
					else 
						return 1;
				}
				
			}));
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			debug.print("[ ERROR ] Can't search (simple)  "+pattern);
			e.printStackTrace();
		}

		return ret;
	}
}
